package com.simple.util.minicode;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import com.sun.org.apache.xml.internal.security.utils.Base64;
import org.apache.commons.lang3.StringUtils;

import javax.imageio.ImageIO;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Hashtable;

/**
 * @program: simple_tools
 * @description: 生成base64的二维码
 * @author: ChenWenLong
 * @create: 2019-12-31 09:33
 **/
public class QRCodeUtil {

    private static final int BLACK = 0xFF000000;// 用于设置图案的颜色
    private static final int WHITE = 0xFFFFFFFF; // 用于背景色

    /**
     * 功能描述:
     * 〈生成base64的二维码〉
     *
     * @params : [content]
     * @return : java.lang.String
     * @author : cwl
     * @date : 2019/12/31 9:34
     */
    public static String crateQRCode(String content) throws IOException {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        try {
            QRCodeWriter writer = new QRCodeWriter();
            BitMatrix bitMatrix = writer.encode(content, BarcodeFormat.QR_CODE, 200, 200);
            BufferedImage bufferedImage = MatrixToImageWriter.toBufferedImage(bitMatrix);
            ImageIO.write(bufferedImage, "png", os);
            //添加图片标识
            return new String("data:image/png;base64," + Base64.encode(os.toByteArray()));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            os.close();
        }
        return null;
    }


    /**
     * 生成图片
     *
     * @param width
     * @param height
     * @param content
     * @param logo
     * @throws IOException
     * @throws WriterException
     */
    public static void writeQRCodeFile(int width, int height, String content, String logo, File outputFile) throws IOException, WriterException {
        Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();
        // 指定纠错等级,纠错级别（L 7%、M 15%、Q 25%、H 30%）
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        // 内容所使用字符集编码
        hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
        hints.put(EncodeHintType.MARGIN, 0);// 设置二维码边的空度，非负数
        BitMatrix bitMatrix = new MultiFormatWriter().encode(content,// 要编码的内容
                BarcodeFormat.QR_CODE, width, // 条形码的宽度
                height, // 条形码的高度
                hints);// 生成条形码时的一些配置,此项可选

        if (!outputFile.getParentFile().exists()) {
            outputFile.getParentFile().mkdirs();
        }
        // 创建图片
        if (!outputFile.exists()) {
            outputFile.createNewFile();
        }
        String format = StringUtils.substringAfterLast(outputFile.getName(), ".");
        // 开始生成图片
        QRCodeUtil.writeQRCodeFile(bitMatrix, format, outputFile, logo, null, null);
    }

    /**
     * 生成图片
     *
     * @param matrix
     * @param format
     * @param file
     * @throws IOException
     */
    public static void writeQRCodeFile(BitMatrix matrix, String format, File file, String logo, Integer onColor, Integer offColor) throws IOException {
        BufferedImage image = toBufferedImage(matrix, onColor, offColor);
        image = setLogo(image, logo);
        if (!ImageIO.write(image, format, file)) {
            throw new IOException("Could not write an image of format " + format + " to " + file);
        }
    }

    /**
     * 设置二维码高宽，颜色
     * @param matrix
     * @param onColor 前景色
     * @param offColor 背景色
     * @return
     */
    private static BufferedImage toBufferedImage(BitMatrix matrix, Integer onColor, Integer offColor) {
        int width = matrix.getWidth();
        int height = matrix.getHeight();
        if(onColor == null){
            onColor = BLACK;
        }
        if(offColor == null){
            offColor = WHITE;
        }
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
                image.setRGB(x, y, (matrix.get(x, y) ? onColor : offColor));
            }
        }
        return image;
    }

    /**
     * 设置 logo
     *
     * @param matrixImage 源二维码图片
     * @return 返回带有logo的二维码图片
     * @throws IOException
     */
    private static BufferedImage setLogo(BufferedImage matrixImage, String logo) throws IOException {
        if (StringUtils.isBlank(logo)) {
            return matrixImage;
        }

        File logoFile = new File(logo);
        if (!logoFile.exists() || logoFile.isDirectory()) {
            return matrixImage;
        }
        /**
         * 读取二维码图片，并构建绘图对象
         */
        Graphics2D g2 = matrixImage.createGraphics();

        int matrixWidth = matrixImage.getWidth();
        int matrixHeigh = matrixImage.getHeight();
        /**
         * 读取Logo图片
         */
        BufferedImage logoImg = ImageIO.read(logoFile);

        // 开始绘制图片
        g2.drawImage(logoImg, (matrixWidth / 5) * 2, (matrixHeigh / 5) * 2, matrixWidth / 5, matrixHeigh / 5, null);// 绘制
        BasicStroke stroke = new BasicStroke(5, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
        g2.setStroke(stroke);// 设置笔画对象
        // 指定弧度的圆角矩形
        RoundRectangle2D.Float round = new RoundRectangle2D.Float((matrixWidth / 5) * 2, (matrixHeigh / 5) * 2, matrixWidth / 5, matrixHeigh / 5, 20, 20);
        g2.setColor(Color.white);
        g2.draw(round);// 绘制圆弧矩形
        // 设置logo 有一道灰色边框
        BasicStroke stroke2 = new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
        g2.setStroke(stroke2);// 设置笔画对象
        RoundRectangle2D.Float round2 = new RoundRectangle2D.Float(((matrixWidth / 5) * 2) + 2, ((matrixHeigh / 5) * 2) + 2, (matrixWidth / 5) - 4, (matrixHeigh / 5) - 4, 20, 20);
        g2.setColor(new Color(128, 128, 128));
        g2.draw(round2);// 绘制圆弧矩形

        g2.dispose();
        matrixImage.flush();
        return matrixImage;
    }

    /**
     * 自定义生成二维码
     *
     * @param width
     * @param height
     * @param content
     * @param avatar
     * @param format
     * @throws IOException
     * @throws WriterException
     */
    public static byte[] customGenaralQRCode(int width, int height, String content, String avatar, String format) throws IOException, WriterException {

        Hashtable<EncodeHintType, Object> hints = new Hashtable<EncodeHintType, Object>();
        // 指定纠错等级,纠错级别（L 7%、M 15%、Q 25%、H 30%）
        hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H);
        // 内容所使用字符集编码
        hints.put(EncodeHintType.CHARACTER_SET, "utf-8");
        hints.put(EncodeHintType.MARGIN, 0);// 设置二维码边的空度，非负数

        BitMatrix bitMatrix = new MultiFormatWriter().encode(content,// 要编码的内容
                BarcodeFormat.QR_CODE, width, // 条形码的宽度
                height, // 条形码的高度
                hints);// 生成条形码时的一些配置,此项可选
        bitMatrix = deleteWhite(bitMatrix);// 删除白边
        // 生成二维码二进制数组
        return writeToBytes(bitMatrix, format, avatar);
    }

    /**
     * 生成图片
     *
     * @param matrix
     * @param format
     * @param logoFile
     * @throws IOException
     */
    private static byte[] writeToBytes(BitMatrix matrix, String format, String logoFile) throws IOException {
        byte[] bytes = null;
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try {
            BufferedImage image = toBufferedImage(matrix, null, null);
            image = setLogo(image, logoFile);
            if (!ImageIO.write(image, format, out)) {
                throw new IOException("Could not write an image of format " + format + " to bytes");
            }
            bytes = out.toByteArray();
        } finally {
            out.close();
        }
        return bytes;
    }

    /**
     * 删除白边
     * */
    private static BitMatrix deleteWhite(BitMatrix matrix) {
        int[] rec = matrix.getEnclosingRectangle();
        int resWidth = rec[2] + 1;
        int resHeight = rec[3] + 1;

        BitMatrix resMatrix = new BitMatrix(resWidth, resHeight);
        resMatrix.clear();
        for (int i = 0; i < resWidth; i++) {
            for (int j = 0; j < resHeight; j++) {
                if (matrix.get(i + rec[0], j + rec[1])) {
                    resMatrix.set(i, j);
                }
            }
        }
        return resMatrix;
    }

    public static void main(String[] args) throws IOException, WriterException {
        writeQRCodeFile(100,100,"测试一下","chenwenlong.jpg",new File("test.jpg"));
    }
}
